﻿using System;
using System.Security.Cryptography;
using System.Text;
using System.Web.Security;

using Azure.Domain.Models;

namespace Azure.Membership
{
    public class AzureMembershipProvider : MembershipProvider
    {
        private readonly IMembershipRepository _repository;

        public AzureMembershipProvider()
        {
            if (LoadRepository == null)
            {
                throw new InvalidOperationException("No Repostory has been registered yet.");
            }

            _repository = LoadRepository();
        }

        public AzureMembershipProvider(IMembershipRepository repository)
        {
            _repository = repository;
        }

        public static Func<IMembershipRepository> LoadRepository
        {
            get; set;
        }

        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            MembershipUser result = null;
            var args = new ValidatePasswordEventArgs(username, password, true);

            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            MembershipUser u = GetUser(username, false);

            if (u == null)
            {
                Guid rowKey;

                if (providerUserKey == null)
                {
                    rowKey = Guid.NewGuid();
                }
                else
                {
                    if (!(providerUserKey is Guid))
                    {
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }

                    rowKey = (Guid)providerUserKey;
                }

                var user = new User
                               {
                                   PartitionKey = username,
                                   RowKey = rowKey.ToString("N"),
                                   Email = email,
                                   PasswordAnswer = passwordAnswer,
                                   PasswordQuestion = passwordQuestion,
                                   UserName = username
                               };

                user.Password = HashPassword(password, user.RowKey);

                _repository.Create(user);

                status = MembershipCreateStatus.Success;
                result = CreateMembershipUser(user);
            }
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }

            return result;
        }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new NotImplementedException();
        }

        public override string GetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }

        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            throw new NotImplementedException();
        }

        public override string ResetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }

        public override void UpdateUser(MembershipUser user)
        {
            throw new NotImplementedException();
        }

        public override bool ValidateUser(string username, string password)
        {
            bool result = false;
            var user = _repository.LoadByUsername(username);
            if (user != null)
            {
                var hashedPassword = HashPassword(password, user.RowKey);
                result = hashedPassword == user.Password;
            }

            return result;
        }

        public override bool UnlockUser(string userName)
        {
            throw new NotImplementedException();
        }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            throw new NotImplementedException();
        }

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            var user = _repository.LoadByUsername(username);
            return CreateMembershipUser(user);
        }

        protected MembershipUser CreateMembershipUser(User user)
        {
            MembershipUser result = null;
            if (user != null)
            {
                result = new MembershipUser(
                    Name,
                    user.UserName,
                    user.PartitionKey,
                    user.Email,
                    user.PasswordQuestion,
                    string.Empty,
                    true,
                    false,
                    user.Timestamp,
                    DateTime.Now,
                    DateTime.Now,
                    DateTime.Now,
                    DateTime.Now);
            }

            return result;
        }

        public override string GetUserNameByEmail(string email)
        {
            string result = String.Empty;
            var user = _repository.LoadByEmail(email);
            
            if (user != null)
            {
                result = user.UserName;
            }

            return result;
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new NotImplementedException();
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }

        public override int GetNumberOfUsersOnline()
        {
            throw new NotImplementedException();
        }

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }

        public override string Name
        {
            get
            {
                string result = base.Name;
                if (String.IsNullOrEmpty(result))
                {
                    result = "AzureMembershipProvider";
                }

                return result;
            }
        }

        public override bool EnablePasswordRetrieval
        {
            get
            {
                return false;
            }
        }

        public override bool EnablePasswordReset
        {
            get
            {
                return true;
            }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get
            {
                return false;
            }
        }

        public override string ApplicationName
        {
            get;
            set;
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return 10; }
        }

        public override int PasswordAttemptWindow
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public override bool RequiresUniqueEmail
        {
            get { return true; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get
            {
                return MembershipPasswordFormat.Hashed;
            }
        }

        public override int MinRequiredPasswordLength
        {
            get { return 5; }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return 2; }
        }

        public override string PasswordStrengthRegularExpression
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public virtual string HashPassword(string password, string salt)
        {
            var sha = new SHA1CryptoServiceProvider();
            byte[] pwdBytes = Encoding.UTF8.GetBytes(salt + password);
            byte[] hashedBytes = sha.ComputeHash(pwdBytes);
            return Convert.ToBase64String(hashedBytes);
        }
    }
}